/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools super block
 */

#include "compare_super_block.h"

#include <memory>
#include <cinttypes>
#include <cmath>
#include <string>
#include <sys/types.h>
#include <fstream>

#include "hmfs_log.h"
#include "hmfs_utils.h"
#include "securec.h"

namespace OHOS {
namespace Hmfs {
int32_t CompareSuperBlockArea(FsData &srcInfo, FsData &dstInfo)
{
    if (GetSuperBlockData(srcInfo)) {
        return -1;
    }

    if (GetSuperBlockData(dstInfo)) {
        return -1;
    }

    if (CompareSuperBlockData(srcInfo.superBlock, dstInfo.superBlock)) {
        HMFS_ERROR("First super block not equal.");
        return -1;
    }

    if (CompareSuperBlockData(srcInfo.superBlock2, dstInfo.superBlock2)) {
        HMFS_ERROR("Second super block not equal.");
        return -1;
    }

    return 0;
}

int32_t GetSuperBlockData(FsData &fsInfo)
{
    std::ifstream file(fsInfo.path.c_str(), std::ios::in);
    if (!file) {
        HMFS_ERROR("Failed to open file, errno %d", errno);
        return -1;
    }

    fsInfo.superBlockBuf = std::make_unique<char[]>(HMFS_BLOCK_SIZE * 2);

    file.read(fsInfo.superBlockBuf.get(), HMFS_BLOCK_SIZE * 2);
    if (!file) {
        HMFS_ERROR("Failed to read super block.");
        return -1;
    }

    fsInfo.superBlock = reinterpret_cast<SuperBlockData*>(fsInfo.superBlockBuf.get() + HMFS_SUPER_BLOCK_OFFSET);
    fsInfo.superBlock2 = reinterpret_cast<SuperBlockData*>(fsInfo.superBlockBuf.get() +
        HMFS_BLOCK_SIZE + HMFS_SUPER_BLOCK_OFFSET);

    for (int32_t qtype = 0; qtype < MAX_QUOTAS; qtype++) {
        if (fsInfo.superBlock->quotaInodeId[qtype] != 0) {
            fsInfo.quotaCount++;
            fsInfo.quotaBits |= 1 << qtype;
        }
    }

    fsInfo.features = GetLeValue(fsInfo.superBlock->features);
    for (uint32_t i = 0; i < MAX_DEVICE_COUNT; i++) {
        if (fsInfo.superBlock->devices[i].path[0] == 0) {
            break;
        }

        DevInfo deviceInfo;
        deviceInfo.path = fsInfo.superBlock->devices[i].path;
        deviceInfo.segmentCount = GetLeValue(fsInfo.superBlock->devices[i].segmentCount);
        if (i == 0) {
            deviceInfo.startBlkId = 0;
            deviceInfo.endBlkId = deviceInfo.segmentCount * BLOCKS_PER_SEGMENT - 1 + GetLeValue(fsInfo.superBlock->segment0BlockId);
        } else {
            DevInfo& prevDevice = fsInfo.deviceList[i - 1];
            deviceInfo.startBlkId = prevDevice.endBlkId + 1;
            deviceInfo.endBlkId = deviceInfo.startBlkId + deviceInfo.segmentCount * BLOCKS_PER_SEGMENT - 1;
        }
        fsInfo.deviceList.emplace_back(deviceInfo);

        HMFS_DEBUG("dev[%u]: segmentCount = %u, startBlkId = %" PRIu64 ", endBlkId = %" PRIu64 ".",
            i, deviceInfo.segmentCount, deviceInfo.startBlkId, deviceInfo.endBlkId);
    }

    return 0;
}

int32_t CompareSuperBlockData(SuperBlockData *srcSuperBlock, SuperBlockData *dstSuperBlock)
{
    if (memcmp(srcSuperBlock, dstSuperBlock, sizeof(SuperBlockData)) == 0) {
        return 0;
    }

    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, magicNumber);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, majorVersion);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, minorVersion);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, logSectorSize);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, log2SectorsPerBlock);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, logBlockSize);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, logBlksPerSeg);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segsPerSection);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, sectionsPerZone);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, checksumOffset);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, blockCount);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, sectionCount);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCount);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCountInCP);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCountInSIT);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCountInNAT);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCountInSSA);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segmentCountInMain);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, segment0BlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, checkPointBlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, sitBlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, natBlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, ssaBlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, mainBlockId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, rootInodeId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, nodeInodeId);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, metaInodeId);
    COMPARE_MEMORY(srcSuperBlock, dstSuperBlock, volumeName);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, coldExtensionCount);
    for (uint32_t i = 0; i < EXTENSION_COUNT_MAX; i++) {
        COMPARE_STRING(srcSuperBlock, dstSuperBlock, extensionList[i], i);
    }
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, checkPointPayload);
    COMPARE_STRING(srcSuperBlock, dstSuperBlock, version);
    COMPARE_STRING(srcSuperBlock, dstSuperBlock, initVersion);
#ifndef HMFS_UNIT_TEST_RESIZE
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, features);
#endif
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, encryptionLevel);
    COMPARE_MEMORY(srcSuperBlock, dstSuperBlock, encryptionSalt);
    for (uint32_t i = 0; i < MAX_DEVICE_COUNT; i++) {
        COMPARE_STRING_NORETURN(srcSuperBlock, dstSuperBlock, devices[i].path, i);
        COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, devices[i].segmentCount, i);
    }
    for (uint32_t i = 0; i < MAX_QUOTAS; i++) {
        COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, quotaInodeId[i], i);
    }
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, hotExtensionCount);
#ifndef HMFS_UNIT_TEST_RESIZE
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, encoding);
    COMPARE_NUMBER(srcSuperBlock, dstSuperBlock, encodingFlags);
    COMPARE_MEMORY(srcSuperBlock, dstSuperBlock, stopReason);
    COMPARE_MEMORY(srcSuperBlock, dstSuperBlock, errors);
    COMPARE_MEMORY(srcSuperBlock, dstSuperBlock, reserved);
#endif
    return 0;
}
} // namespace Hmfs
} // namespace OHOS
